<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="cmd.css" rel="stylesheet" type="text/css">
<title>GRUB4DOS dd、write参数</title>
</head>

<body>
<p>摘自 <a href="http://bbs.znpc.net/viewthread.php?tid=6009&page=3&fromuid=12697#pid46551" target="_blank">时空论坛</a> （by 不点@znpc）</p>
<blockquote>
  <p>dd 命令我也没有用过，我上次是用 cat 显示在屏幕上，然后用手抄的。</p>
  <p>好的，现在咱们一起学习一下 dd 命令。先看 dd 命令的帮助文档：</p>
  <blockquote>
    <p>&quot;dd if=IF of=OF [bs=BS] [count=C] [skip=IN] [seek=OUT] [buf=ADDR] [buflen=SIZE]&quot;,<br>
      &quot;Copy file IF to OF. BS is blocksize, default to 512. C is blocks to copy,&quot;<br>
      &quot; default is total blocks in IF. IN specifies number of blocks to skip when&quot;<br>
      &quot; read, default is 0. OUT specifies number of blocks to skip when write,&quot;<br>
      &quot; default is 0. Skipped blocks are not touched. Both IF and OF must exist.&quot;<br>
      &quot; dd can neither enlarge nor reduce the size of OF, the leftover tail&quot;<br>
      &quot; of IF will be discarded. OF cannot be a gzipped file. If IF is a gzipped&quot;<br>
      &quot; file, it will be decompressed automatically when copying. dd is dangerous,&quot;<br>
      &quot; use at your own risk. To be on the safe side, you should only use dd to&quot;<br>
      &quot; write a file in memory. ADDR and SIZE are used for user-defined buffer.&quot;</p>
  </blockquote>
</blockquote>
<p class="style1">  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个说明中，有暗示说，dd 有危险，建议只写入内存文件。我们是要读内存文件，而写入磁盘文件，所以，还是有危险的。为了尽量安全，你最好只写入 U 盘上的文件。因为 U 盘写坏了还可以接受，但硬盘写坏了，那就不可接受了。如果你读一个文件很正常，那么你写入它，一般也不会有问题。所以，硬盘上的文件也可以写入，只要你读它的时候很正常（例如用 cat --hex来显示它），就比较放心了。但要注意，不要写入到不该写入的文件了，文件名不要搞错，文件的路径也不要搞错。</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【补充一下：安全第一！！用 dd 命令时，千万不能把方向搞错！！ if 是 input file，也就是要读的文件。of 是 output file，也就是要写的文件。不要为 of 填写了一个光秃秃的设备名 (hd0) 或者 (hd0,0)！！那可糟糕了！！在 (hd0) 的情形，整个硬盘从 MBR 都要被写入，完蛋透顶了！！！！在 (hd0,0) 的情形，这个分区完蛋了，也够你坐地上哭一场的！！所以，你一定要记得在设备名后紧跟文件路径，例如，(hd0,0)/my_temp_file.bin，这样就不会出大问题了。】</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="Options">IF</span> 是要读的文件。你填写的是 <span class="bat">(rd)</span> ，这是对的。我们要使用 (rd) 设备了。但是，注意，(rd) 是一个活动的设备，它的内存基地址不是固定的，而是可变的。为了确定起见，我们首先应该保证 (rd) 的基地址是已知的，（比如，我们知道它的基地址是 0），那么我们后续的工作才可以做。用 <span class="bat">map --status </span>命令能够显示 rd 的基地址和长度。其中，<span class="bat">rd_base ＝ 0x0</span> 就表示 rd 的基地址为 0，<span class="bat">rd_size = 0x100000000</span> 就表示 rd 的长度是 4G。如果基地址不是 0，或者长度不是 4G，你需要用下面的命令来设置它：</p>
<blockquote>
  <blockquote>
    <p><span class="cmd">map --rd-base=0</span><br>
      <span class="cmd">map --rd-size=0x100000000</span></p>
  </blockquote>
</blockquote>
<p class="style1">这样设置之后，rd 设备的内容就变了。所以，先前某个程序在使用的那个旧的 rd 设备就不存在了，或者说，它的内容已经变成新的 rd 设备的内容了。因此，等你操作完成之后，你应该恢复 rd 原来的基地址和长度。如果你懒得做，也可以简单地重启电脑。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好了，以上都是废话。现在开始进入正题了。你需要截获的是两块内存：</p>
<blockquote>
  <blockquote>
    <p><span class="cmd">FACP table at: 0x3f7c0200,length is 0x84</span><br>
      <span class="cmd">DSDT table at: 0x3f7c05b0,length is 0x42fc</span></p>
  </blockquote>
</blockquote>
<p class="style1">这两块内存靠得很近，为了简单起见，我们可以把它看成一大块内存。粗略来讲，我们只需要截获</p>
<blockquote>
  <blockquote>
    <p><strong>0x3f7c0000，长度为 0x5000</strong></p>
  </blockquote>
</blockquote>
<p class="style1">便可以了。这一大块区域，就包括了上面的两小块区域。注意，这个十六进制的长度值 0x5000 也就是 20K。因此，你也需要一个至少有 20K 的文件才能装得下它。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;真正有用的 dd 参数是如下这几个。而 buf 和 buflen 是很少用到的，我们可以不管它。</p>
<p class="Options">  dd if=IF of=OF [bs=BS] [count=C] [skip=IN] [seek=OUT] </p>
<p class="style1">你已经把 IF 和 OF 都填写完了。你准备的<span class="Options"> OF</span> 文件是 <span class="bat">(hd0,0)/temp/dsdt.txt</span>，它有 300K，那就太大了，主要是因为，将来你上载它的时候，论坛不一定让你上载这么大的一个文件。所以，你最好重新建立一个 20K 的文件，不多不少，刚好够用便可。20K 就是 20 乘 1024 个字节，也就是 20480 个字节。</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下一个要学习的参数是<span class="Options"> bs</span>，它指定读写所用的单位（bs 就是 block size，块大小）。默认时，bs=512，就是以扇区为单位来计算。我们设置成 1 更方便一些。</p>
<p><span class="cmd">dd if=(rd) of=(hd0,0)/temp/dsdt.txt bs=1</span></p>
<p class="style1">块的大小设置成 1 了，那么我们就以字节为单位了。因此，后续的三个参数 count，skip 和 seek 也都是以字节来计算了。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="Options">count</span> 是要读取的块数（由于每个块是一个字节，所以，块数也就是字节数，下同）。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="Options">skip</span> 是要跳过的块数（即字节数）。</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="Options">&nbsp;seek </span>是写入到目的文件时，要跳过的目的文件的块数（即字节数），也就是从文件的中间开始写入，而不是从文件的开头来写。</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们需要把内存区域写入到一个 20K 的文件中，这占满了整个 20K 的文件，因此，不能跳过任何字节，我们需要的seek 值是 0，也就是要写入文件的开头，一直写到文件的结尾，总共要写 20K。而 seek 默认就是 0，因此我们不用填写它。count 很容易确定，我们需要写入的块数（即字节数）是 20K，也就是 0x5000。</p>
<p><span class="cmd">dd if=(rd) of=(hd0,0)/temp/dsdt.txt bs=1 count=0x5000</span></p>
<p><span class="style1">但是，skip 参数也需要填写。如果不填写 skip 参数，那么 skip 是 0。也就是说，从 (rd) 的开头开始读。而我们不是要从地址 0 开始读，而是要从地址 0x3f7c0000 开始读。因此，我们要跳过 0x3f7c0000 个字节。</span><br>
  <br>
<span class="cmd">dd if=(rd) of=(hd0,0)/temp/dsdt.txt bs=1 count=0x5000 skip=0x3f7c0000</span></p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;太好了，我也学会了，以后，我再也不用麻烦 cat 命令来显示然后再手工抄写了，那确实很累人的，我亲身体会：先抄到纸上，再敲到电脑上，是两次抄写，每一次都容易出错，整个过程下来，满满地需要半天。</p>
<p class="style1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后补充一点：of=(hd0,0)/temp/dsdt.txt 中的 of 是小写的英文字母，不是数字 0 和 f，也不是大写的英文字母 O。还应该明确强调一次：上述 dd 命令能够正确读取内存的前提是，你已经保证了 (rd) 的基地址为 0，而且长度为 4G。</p>
</body>
</html>
